﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;

namespace Mall.Infrastructure.EnityFramework
{
	internal class ConditionBuilder : ExpressionVisitor
	{
		private List<object> m_arguments;
		private Stack<string> m_conditionParts;

		public string Condition { get; private set; }

		public object[] Arguments { get; private set; }

		public void Build(Expression expression)
		{
			PartialEvaluator evaluator = new PartialEvaluator();
			Expression evaluatedExpression = evaluator.Eval(expression);

			this.m_arguments = new List<object>();
			this.m_conditionParts = new Stack<string>();

			this.Visit(evaluatedExpression);

			this.Arguments = this.m_arguments.ToArray();
			this.Condition = this.m_conditionParts.Count > 0 ? this.m_conditionParts.Pop() : null;
		}

		protected override Expression VisitBinary(BinaryExpression b)
		{
			if (b == null) return b;
            string right, left;

			string opr;
			switch (b.NodeType)
			{
				case ExpressionType.Equal:
					opr = "=";
                    if (b.Right.NodeType == ExpressionType.Constant)
                    {
                        if (((ConstantExpression)b.Right).Value == null)
                        {
                            opr = "is";
                            this.Visit(b.Left);

                            left = this.m_conditionParts.Pop();

                            this.m_conditionParts.Push(String.Format("({0} {1} null)", left, opr, null));

                            return b;
                        }
                    }
					break;
				case ExpressionType.NotEqual:
                    opr = "<>";
                    if (b.Right.NodeType == ExpressionType.Constant)
                    {
                        if (((ConstantExpression)b.Right).Value == null)
                        {
                            opr = "is not";

                            this.Visit(b.Left);

                            left = this.m_conditionParts.Pop();

                            this.m_conditionParts.Push(String.Format("({0} {1} null)", left, opr));

                            return b;
                        }
                    }
					break;
				case ExpressionType.GreaterThan:
					opr = ">";
					break;
				case ExpressionType.GreaterThanOrEqual:
					opr = ">=";
					break;
				case ExpressionType.LessThan:
					opr = "<";
					break;
				case ExpressionType.LessThanOrEqual:
					opr = "<=";
					break;
				case ExpressionType.AndAlso:
					opr = "AND";
					break;
				case ExpressionType.OrElse:
					opr = "OR";
					break;
				case ExpressionType.Add:
					opr = "+";
					break;
				case ExpressionType.Subtract:
					opr = "-";
					break;
				case ExpressionType.Multiply:
					opr = "*";
					break;
				case ExpressionType.Divide:
					opr = "/";
					break;
				default:
					throw new NotSupportedException(b.NodeType + " is not supported.");
			}

			this.Visit(b.Left);
			this.Visit(b.Right);


			right = this.m_conditionParts.Pop();
			left = this.m_conditionParts.Pop();

			string condition = String.Format("({0} {1} {2})", left, opr, right);
			this.m_conditionParts.Push(condition);

			return b;
		}

		protected override Expression VisitConstant(ConstantExpression c)
		{
			if (c == null) return c;

			this.m_arguments.Add(c.Value);
			this.m_conditionParts.Push(String.Format("{{{0}}}", this.m_arguments.Count - 1));

			return c;
		}

		protected override Expression VisitMemberAccess(MemberExpression m)
		{
			if (m == null) return m;

			PropertyInfo propertyInfo = m.Member as PropertyInfo;
			if (propertyInfo == null) return m;

			this.m_conditionParts.Push(String.Format("[{0}]", propertyInfo.Name));

			return m;
		}

		protected override Expression VisitMethodCall(MethodCallExpression m)
		{
            string right, left;
			if (m == null) return m;
			string format;
            Expression Object = m.Object;
			switch (m.Method.Name)
			{
				case "StartsWith":
					format = "({0} LIKE {1}+'%')";
					break;

				case "Contains":
                    //扩展数组
                    if (m.Object.Type == typeof(List<int>))
                    {
                        var ss = "";
                        var list = ((ConstantExpression)m.Object).Value as List<int>;
                        list.ForEach(mm => {
                            ss += mm + ",";
                        });
                        ss = ss.Substring(0, ss.Length - 1);
                        format ="({0} in ({1}))";

			            this.Visit(m.Arguments[0]);
			            left = this.m_conditionParts.Pop();
			            this.m_conditionParts.Push(String.Format(format, left, ss));
			            return m;
                    }
                    else
                    {
                        format = "({0} LIKE '%'+{1}+'%')";
                    }
					break;

				case "EndsWith":
					format = "({0} LIKE '%'+{1})";
					break;

				default:
					throw new NotSupportedException(m.NodeType + " is not supported!");
			}

			this.Visit(Object);
			this.Visit(m.Arguments[0]);
			right = this.m_conditionParts.Pop();
			left = this.m_conditionParts.Pop();
			this.m_conditionParts.Push(String.Format(format, left, right));

			return m;
		}
	}
}
